/*
 * Copyright 2023 devaper
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.github.devaper.pigeon.factory

import io.github.devaper.pigeon.*
import io.github.devaper.pigeon.exception.*
import io.github.devaper.pigeon.handler.argument.*
import io.github.devaper.pigeon.handler.method.*
import io.github.devaper.pigeon.request.*
import io.github.devaper.pigeon.response.*
import io.github.devaper.pigeon.support.*
import java.lang.reflect.*
import java.util.*

/**
 * 实现了通用功能的HttpClientFactory, 关键实现交给子类
 */
abstract class AbstractHttpClientFactory : ClientFactory {

    override fun <T> createClient(clientConfig: ClientConfig, clientInterface: Class<T>): T {
        // 校验fallback是否是client的实现类
        if (clientConfig.fallbackInstance != null) {
            val fallbackClass = clientConfig.fallbackInstance!!.javaClass
            if (!clientInterface.isAssignableFrom(fallbackClass)) {
                throw ClassCastException("Fallback [${fallbackClass.canonicalName}] must implement [${clientInterface.canonicalName}]")
            }
        }
        val methodHandlerMapping = obtainMethodHandlerMapping(
            clientConfig = clientConfig.clone(),
            clientInterface = clientInterface
        )
        val clientInvocationHandler = SimpleClientInvocationHandler(methodHandlerMapping = methodHandlerMapping)
        return createInstance(clientInterface, clientInvocationHandler)
    }

    /**
     * 为Client创建 [Method] 与 [MethodHandler] 的映射
     *
     * @param clientConfig [ClientConfig]
     * @param clientInterface Client接口
     * @return [MethodHandlerMapping]
     */
    protected open fun obtainMethodHandlerMapping(
        clientConfig: ClientConfig,
        clientInterface: Class<*>
    ): MethodHandlerMapping {
        // 为每个方法创建一个MethodHandler
        val methods = clientInterface.methods
        val mapping = methods.associateWithTo(HashMap(methods.size)) {
            createMethodHandler(clientConfig = clientConfig, method = it)
        }
        return MethodHandlerMapping(mapping)
    }

    /**
     * 针对一个 [Method] 创建一个 [AbstractHttpMethodHandler], 最终对该方法的调用会转发到 [AbstractHttpMethodHandler]
     *
     * @param clientConfig [ClientConfig]
     * @param method [Method] jvm方法
     * @return [AbstractHttpMethodHandler]
     */
    open fun createMethodHandler(clientConfig: ClientConfig, method: Method): AbstractHttpMethodHandler {
        val methodContext = createMethodContext(method)
        val requestBasicInfo = resolveRequestBasicInfo(clientConfig, method)
        // 创建与参数数量一致的参数处理器
        val argumentHandlers = methodContext.parameters.map { getArgumentHandler(it) }
        return createHttpMethodHandler(
            clientConfig = clientConfig,
            methodContext = methodContext,
            requestBasicInfo = requestBasicInfo,
            argumentHandler = ArgumentHandlerComposite(argumentHandlers)
        )
    }

    /**
     * 创建代理实例
     *
     * @param clientInterface Client接口
     * @param clientInvocationHandler [InvocationHandler]
     * @return 代理实例
     */
    @Suppress("unchecked_cast")
    protected open fun <T> createInstance(clientInterface: Class<T>, clientInvocationHandler: InvocationHandler): T {
        return Proxy.newProxyInstance(clientInterface.classLoader, arrayOf(clientInterface), clientInvocationHandler) as T
    }

    /**
     * 根据 [Method] 创建它的 [MethodContext]
     *
     * @param method [Method] jvm方法
     * @return [MethodContext]
     */
    protected open fun createMethodContext(method: Method): MethodContext {
        // 强制要求返回类型必须为Deferred
        if (!Deferred::class.java.isAssignableFrom(method.returnType)) {
            throw IllegalStateException("The return value must be Deferred")
        }
        val actualReturnType = (method.genericReturnType as ParameterizedType).actualTypeArguments.first()
        return MethodContext(
            method = method,
            actualType = actualReturnType,
            parameters = Collections.unmodifiableList(method.parameters.toList()),
        )
    }

    /**
     * 通过 [Method] 解析出基本的请求信息, 与 [AbstractHttpMethodHandler] 相绑定
     *
     * @param clientConfig [ClientConfig]
     * @param method [Method] jvm方法
     * @return [RequestBasicInfo]
     */
    protected abstract fun resolveRequestBasicInfo(clientConfig: ClientConfig, method: Method): RequestBasicInfo

    /**
     * 根据参数创建参数处理器, 如果无法处理请抛出异常 [CreateArgumentHandlerFailureException] 而不是返回null,
     * 子类尽量将参数处理器从设计上支持复用, 创建时采用缓存, 减少资源占用
     *
     * @param parameter [Parameter] jvm方法参数
     * @return [ArgumentHandler]
     */
    @Throws(CreateArgumentHandlerFailureException::class)
    protected abstract fun getArgumentHandler(parameter: Parameter): ArgumentHandler

    /**
     * 创建发送Http请求的MethodHandler
     *
     * @param clientConfig [ClientConfig]
     * @param methodContext [MethodContext]
     * @param requestBasicInfo [RequestBasicInfo]
     * @param argumentHandler [ArgumentHandler]
     * @return [AbstractHttpMethodHandler]
     */
    protected abstract fun createHttpMethodHandler(
        clientConfig: ClientConfig,
        methodContext: MethodContext,
        requestBasicInfo: RequestBasicInfo,
        argumentHandler: ArgumentHandler
    ): AbstractHttpMethodHandler
}